Assume volatilidade constante durante a janela. (ou seja precisa de um range dias e não é capaz de medir o desvio ou o risco/volatilidade de um dia pro outro.)
Não capta a persistência dos choques (efeito de clustering).
Não reage dinamicamente a choques recentes.
Volatilidade com o GARCH
O modelo GARCH(1,1) estima a variância condicional de forma dinâmica:
Onde: - \(\epsilon_{t-1}^2\) reflete o impacto dos choques recentes. - \(\sigma_{t-1}^2\) reflete a persistência da volatilidade do período anterior. - \(\omega > 0\), \(\alpha \geq 0\) e \(\beta \geq 0\) são parâmetros estimados.
A soma \(\alpha + \beta\) mede a persistência total da volatilidade:
Próximo de 1: Choques têm efeitos duradouros; a volatilidade permanece alta por vários períodos.
Menor que 1: Os choques se dissipam mais rapidamente; a volatilidade retorna ao seu nível médio mais rápido.
Vantagens do GARCH:
Modela a volatilidade de forma dinâmica.
Captura o efeito de “clustering” dos choques.
Permite previsões mais precisas da volatilidade futura.
Desvantagens do GARCH:
Requer estimação de parâmetros e pressupõe uma estrutura específica para a volatilidade.
Pode ser sensível à escolha da distribuição dos resíduos (por exemplo, normal vs. \(t\) de Student).
A volatilidade histórica pode ser medida como o desvio-padrão dos log-retornos calculado em uma janela móvel de \(N\) dias de negociação.
Neste exemplo, adotamos uma janela móvel de 5 dias (aproximadamente uma semana de negociação. Para um mês de negociação, utilizar 22 e um ano 252) para capturar a volatilidade diária dos ativos.
Vantagens:
Simplicidade e facilidade de implementação.
Desvantagens:
Assume volatilidade constante durante a janela.
Não capta a persistência dos choques.
Não reage dinamicamente a choques recentes.
Code
# Calcular a volatilidade histórica com uma janela móvel de 5 dias# Supondo que 'log_returns' já foi calculado e possui a coluna 'date' e os log-retornos dos ativoswindow =5# Cria um DataFrame para armazenar a volatilidade históricavol_hist = pd.DataFrame({'date': log_returns["date"]})# Calcula o desvio-padrão móvel (volatilidade) para cada ativofor col in log_returns.columns[1:]: vol_hist[col] = log_returns[col].rolling(window=window).std()# Exibe as ultimas linhas do DataFrame de volatilidade histórica#print(vol_hist.head()) # 5 primeiros serão NaNprint(vol_hist.tail())
# Retire o comando #| eval: false pra conseguir executar essa celula dentro do Quarto# Certificar que "date" é datetime (já feito)vol_hist['date'] = pd.to_datetime(vol_hist['date'])# Transformar para formato longovol_hist_long = vol_hist.melt(id_vars='date', var_name='Ativo', value_name='Volatilidade_Hist')fig = px.line(vol_hist_long, x='date', y='Volatilidade_Hist', color='Ativo', title='Volatilidade Histórica (Desvio-Padrão) com janela de 5 dias')fig.show()
O desvio-padrão assume que precisaremos de um range maior do que 2 pontos no tempo, o que limita nossa análise pois a incompatibiliza, uma vez que precisaremos comparar os riscos diários x retornos diários (como feito anteriormente). Ou seja, não podemos comparar retornos dia-a-dia x volatilidades (risco) de 5 em 5 dias p. ex.
Os modelos heterocedásticos (da família ARCH) estimam a variância condicional dos nossos dados, ou seja, em linguagem de finanças, eles são capazes de capturar as volatilidades ou risco dos retornos dos preços de ativos financeiros ponto a ponto no tempo, ou seja, dia a dia.
O modelo GARCH(1,1) com distribuição \(t\) assimétrica não está disponível diretamente na maioria das bibliotecas Python. No entanto, podemos utilizar um GARCH(1,1) com uma distribuição \(t\) padrão para estimar a variância condicional. O modelo é representado por:
\(\epsilon_t\) é o termo de erro, condicionado às informações passadas.
\(\sigma_t^2\) é a variância condicional no tempo \(t\).
\(\omega, \alpha, \beta\) são os parâmetros a serem estimados, com \(\omega > 0, \alpha \geq 0, \beta \geq 0\).
\(z_t\) segue uma distribuição \(t\) de Student com \(ν\) graus de liberdade para capturar as caudas pesadas observadas em retornos financeiros.
A soma \(\alpha + \beta\) é frequentemente utilizada para medir a persistência da volatilidade: quanto mais próximos de 1, maior a persistência dos choques na volatilidade.
Vamos estimar a variância condicional (\(\sigma^2_{t}\) ) para cada ativo:
Code
# Estimar o modelo GARCH(1,1) e salvar variância condicionalvar_condicional = pd.DataFrame({"date": log_returns["date"]})for col in log_returns.columns[1:]: am = arch_model(log_returns[col], vol="Garch", p=1, q=1, dist="t") res = am.fit(disp="off") var_condicional[col] = res.conditional_volatility **2var_condicional.head()
C:\Users\kuiav\anaconda3\Lib\site-packages\arch\univariate\base.py:309: DataScaleWarning: y is poorly scaled, which may affect convergence of the optimizer when
estimating the model parameters. The scale of y is 0.0007186. Parameter
estimation work better when this value is between 1 and 1000. The recommended
rescaling is 100 * y.
This warning can be disabled by either rescaling y before initializing the
model or by setting rescale=False.
C:\Users\kuiav\anaconda3\Lib\site-packages\arch\univariate\base.py:309: DataScaleWarning: y is poorly scaled, which may affect convergence of the optimizer when
estimating the model parameters. The scale of y is 0.0009502. Parameter
estimation work better when this value is between 1 and 1000. The recommended
rescaling is 100 * y.
This warning can be disabled by either rescaling y before initializing the
model or by setting rescale=False.
C:\Users\kuiav\anaconda3\Lib\site-packages\arch\univariate\base.py:309: DataScaleWarning: y is poorly scaled, which may affect convergence of the optimizer when
estimating the model parameters. The scale of y is 0.0001738. Parameter
estimation work better when this value is between 1 and 1000. The recommended
rescaling is 100 * y.
This warning can be disabled by either rescaling y before initializing the
model or by setting rescale=False.
C:\Users\kuiav\anaconda3\Lib\site-packages\arch\univariate\base.py:309: DataScaleWarning: y is poorly scaled, which may affect convergence of the optimizer when
estimating the model parameters. The scale of y is 0.0002101. Parameter
estimation work better when this value is between 1 and 1000. The recommended
rescaling is 100 * y.
This warning can be disabled by either rescaling y before initializing the
model or by setting rescale=False.
C:\Users\kuiav\anaconda3\Lib\site-packages\arch\univariate\base.py:309: DataScaleWarning: y is poorly scaled, which may affect convergence of the optimizer when
estimating the model parameters. The scale of y is 0.0004777. Parameter
estimation work better when this value is between 1 and 1000. The recommended
rescaling is 100 * y.
This warning can be disabled by either rescaling y before initializing the
model or by setting rescale=False.
C:\Users\kuiav\anaconda3\Lib\site-packages\arch\univariate\base.py:309: DataScaleWarning: y is poorly scaled, which may affect convergence of the optimizer when
estimating the model parameters. The scale of y is 0.0008154. Parameter
estimation work better when this value is between 1 and 1000. The recommended
rescaling is 100 * y.
This warning can be disabled by either rescaling y before initializing the
model or by setting rescale=False.
C:\Users\kuiav\anaconda3\Lib\site-packages\arch\univariate\base.py:309: DataScaleWarning: y is poorly scaled, which may affect convergence of the optimizer when
estimating the model parameters. The scale of y is 0.0002983. Parameter
estimation work better when this value is between 1 and 1000. The recommended
rescaling is 100 * y.
This warning can be disabled by either rescaling y before initializing the
model or by setting rescale=False.
date
BEEF3.SA
BRFS3.SA
GIS
HRL
JBSS3.SA
MRFG3.SA
TSN
1
2020-04-14
0.006489
0.003739
0.000189
0.002342
0.000879
0.001129
0.002966
2
2020-04-15
0.005795
0.003823
0.000202
0.002844
0.000781
0.000866
0.002396
3
2020-04-16
0.005688
0.003715
0.000179
0.002033
0.000779
0.000740
0.002656
4
2020-04-17
0.007190
0.003715
0.000222
0.002954
0.000672
0.000705
0.002138
5
2020-04-20
0.006506
0.003712
0.000190
0.002054
0.000642
0.000673
0.002299
Vamos avaliar os parâmetros estimados do modelo:
Code
# Inferir sobre os parâmetros do modelo GARCH(1,1) para cada ativo do portfólioparams_list = []# Iterar sobre cada ativo (exceto a coluna 'date')for col in log_returns.columns[1:]: am = arch_model(log_returns[col], vol="Garch", p=1, q=1, dist="t") res = am.fit(disp="off") par = res.params alpha_val = par.get("alpha[1]", None) beta_val = par.get("beta[1]", None) alpha_beta_sum = (alpha_val if alpha_val isnotNoneelse0) + (beta_val if beta_val isnotNoneelse0)# Interpretação curtaif alpha_beta_sum >=0.9: interp =f"Alta persistência (α+β = {alpha_beta_sum:.4f})."else: interp =f"Baixa/moderada persistência (α+β = {alpha_beta_sum:.4f})." params_list.append({"Ativo": col,"mu": par.get("mu", None),"omega": par.get("omega", None),"alpha": alpha_val,"beta": beta_val,"alpha+beta": alpha_beta_sum,"nu": par.get("nu", None),"Interpretacao": interp })garch_params = pd.DataFrame(params_list)
C:\Users\kuiav\anaconda3\Lib\site-packages\arch\univariate\base.py:309: DataScaleWarning: y is poorly scaled, which may affect convergence of the optimizer when
estimating the model parameters. The scale of y is 0.0007186. Parameter
estimation work better when this value is between 1 and 1000. The recommended
rescaling is 100 * y.
This warning can be disabled by either rescaling y before initializing the
model or by setting rescale=False.
C:\Users\kuiav\anaconda3\Lib\site-packages\arch\univariate\base.py:309: DataScaleWarning: y is poorly scaled, which may affect convergence of the optimizer when
estimating the model parameters. The scale of y is 0.0009502. Parameter
estimation work better when this value is between 1 and 1000. The recommended
rescaling is 100 * y.
This warning can be disabled by either rescaling y before initializing the
model or by setting rescale=False.
C:\Users\kuiav\anaconda3\Lib\site-packages\arch\univariate\base.py:309: DataScaleWarning: y is poorly scaled, which may affect convergence of the optimizer when
estimating the model parameters. The scale of y is 0.0001738. Parameter
estimation work better when this value is between 1 and 1000. The recommended
rescaling is 100 * y.
This warning can be disabled by either rescaling y before initializing the
model or by setting rescale=False.
C:\Users\kuiav\anaconda3\Lib\site-packages\arch\univariate\base.py:309: DataScaleWarning: y is poorly scaled, which may affect convergence of the optimizer when
estimating the model parameters. The scale of y is 0.0002101. Parameter
estimation work better when this value is between 1 and 1000. The recommended
rescaling is 100 * y.
This warning can be disabled by either rescaling y before initializing the
model or by setting rescale=False.
C:\Users\kuiav\anaconda3\Lib\site-packages\arch\univariate\base.py:309: DataScaleWarning: y is poorly scaled, which may affect convergence of the optimizer when
estimating the model parameters. The scale of y is 0.0004777. Parameter
estimation work better when this value is between 1 and 1000. The recommended
rescaling is 100 * y.
This warning can be disabled by either rescaling y before initializing the
model or by setting rescale=False.
C:\Users\kuiav\anaconda3\Lib\site-packages\arch\univariate\base.py:309: DataScaleWarning: y is poorly scaled, which may affect convergence of the optimizer when
estimating the model parameters. The scale of y is 0.0008154. Parameter
estimation work better when this value is between 1 and 1000. The recommended
rescaling is 100 * y.
This warning can be disabled by either rescaling y before initializing the
model or by setting rescale=False.
C:\Users\kuiav\anaconda3\Lib\site-packages\arch\univariate\base.py:309: DataScaleWarning: y is poorly scaled, which may affect convergence of the optimizer when
estimating the model parameters. The scale of y is 0.0002983. Parameter
estimation work better when this value is between 1 and 1000. The recommended
rescaling is 100 * y.
This warning can be disabled by either rescaling y before initializing the
model or by setting rescale=False.
A soma \(\alpha + \beta\) é um indicador crucial na modelagem GARCH para avaliar a persistência da volatilidade. Em termos práticos, os parâmetros \(\alpha\) e \(\beta\) têm funções distintas:
\(\alpha\): Representa o impacto dos choques recentes (a inovação ou termo de erro \(\epsilon_{t-1}^2\) na volatilidade atual. Um valor mais alto de \(\alpha\) indica que choques recentes têm um efeito maior em aumentar a volatilidade.
\(\beta\): Captura a persistência da volatilidade ao longo do tempo, ou seja, o efeito da volatilidade passada (\(\sigma_{t-1}^2)\) sobre a volatilidade presente. Valores maiores de \(\beta\) sugerem que a volatilidade tende a se manter elevada por um período mais longo.
Quando somamos esses dois parâmetros, ou seja, quando calculamos \(\alpha + \beta\), obtemos uma medida da persistência total da volatilidade:
Se \(\alpha + \beta\) estiver próximo de 1, isso indica que os choques que afetam a volatilidade têm efeitos de longa duração. Em outras palavras, um choque na volatilidade tem um impacto que se dissipa muito lentamente, mantendo a volatilidade elevada por vários períodos.
Se \(\alpha + \beta\) for significativamente menor que 1, os efeitos dos choques são de curta duração e a volatilidade retorna rapidamente ao seu nível médio após um impacto.
Em alguns casos, quando \(\alpha + \beta = 1\), o modelo é denominado IGARCH (Integrated GARCH), o que implica que os choques têm efeitos persistentes permanentemente, ou seja, a volatilidade não reverte para um valor médio fixo.
Esta característica é particularmente importante na análise de séries financeiras, pois a persistência alta da volatilidade pode implicar maior risco de mercado e desafios na previsão dos retornos futuros. Assim, a soma \(\alpha + \beta\) serve como uma medida de “memória” dos choques, indicando se a volatilidade reage de forma passageira ou duradoura a eventos inesperados.
Graficamente temos:
Code
# Retire o comando #| eval: false pra conseguir executar essa celula dentro do Quartoimport plotly.graph_objects as gofrom plotly.subplots import make_subplots# Para o ativo "ZC=F"returns_zc = log_returns[['date', 'BRFS3.SA']].copy()vol_zc = var_condicional[['date', 'BRFS3.SA']].copy()# Converter "date" para datetime, se necessárioreturns_zc['date'] = pd.to_datetime(returns_zc['date'])vol_zc['date'] = pd.to_datetime(vol_zc['date'])# Criar figura com dois subplots compartilhando o eixo xfig = make_subplots( rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.05, subplot_titles=("Retornos Diários - BRFS3.SA", "Volatilidade Condicional (GARCH) - BRFS3.SA"))# Adicionar o gráfico de retornosfig.add_trace( go.Scatter(x=returns_zc['date'], y=returns_zc['BRFS3.SA'], mode='lines', name='Retornos'), row=1, col=1)# Adicionar o gráfico de volatilidade condicionalfig.add_trace( go.Scatter(x=vol_zc['date'], y=vol_zc['BRFS3.SA'], mode='lines', name='Volatilidade'), row=2, col=1)fig.update_layout( height=600, width=900, title_text="Retorno vs. Volatilidade (GARCH) - BRFS3.SA", xaxis2_title="Data", yaxis1_title="Retorno", yaxis2_title="Volatilidade Condicional")fig.show()
Em alguns casos, a variância condicional pode apresentar grandes oscilações se houver outliers nos retornos ou problemas de convergência do modelo. Verifique:
Qualidade e limpeza dos dados
Resumo do ajuste (parâmetros \(\alpha,\beta\) plausíveis?)
Distribuição (\(t\) vs. normal)
Modelos alternativos (EGARCH, GJR-GARCH, etc.)
Aqui iremos visualizar o comportamento do ativo ZC=F, futuros de milho:
Code
import plotly.graph_objects as gofrom plotly.subplots import make_subplots# Para o ativo "ZC=F"returns_zc = log_returns[['date', 'BRFS3.SA']].copy()vol_zc = var_condicional[['date', 'BRFS3.SA']].copy()# Converter "date" para datetime, se necessárioreturns_zc['date'] = pd.to_datetime(returns_zc['date'])vol_zc['date'] = pd.to_datetime(vol_zc['date'])# Criar figura com dois subplots compartilhando o eixo xfig = make_subplots( rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.05, subplot_titles=("Retornos Diários - BRFS3.SA", "Volatilidade Condicional (GARCH) - BRFS3.SA"))# Adicionar o gráfico de retornosfig.add_trace( go.Scatter(x=returns_zc['date'], y=returns_zc['BRFS3.SA'], mode='lines', name='Retornos'), row=1, col=1)# Adicionar o gráfico de volatilidade condicionalfig.add_trace( go.Scatter(x=vol_zc['date'], y=vol_zc['BRFS3.SA'], mode='lines', name='Volatilidade'), row=2, col=1)fig.update_layout( height=600, width=900, title_text="Retorno vs. Volatilidade (GARCH) - BRFS3.SA", xaxis2_title="Data", yaxis1_title="Retorno", yaxis2_title="Volatilidade Condicional")fig.show()
Notem como o GARCH(1,1) captura bem os picos/quebras e na volatilidade dos retornos.
References
Gujarati, D. N., & Porter, D. C. (2009). Basic econometrics (5th ed.). McGraw-Hill.
Hyndman, R.J., & Athanasopoulos, G. (2021) Forecasting: principles and practice, 3rd edition, OTexts: Melbourne, Australia. OTexts.com/fpp3. Accessed on march 2025.